BOM(浏览器对象模型)
定时器——延时函数
语法
setTimeout(回调函数,等待的毫秒数)
清楚延时函数
let timer = setTimeout(回调函数,等待的毫秒数)
clearTimeout(timer)
注意点
setTimeout(function(){
console.log("只运行一次")
},3000)
JS执行机制
由于主线程不断的重复获得任务、执行任务、再获得任务、再执行,所以这种机制被称为事件循环
location对象
<body>
<a href="https://www.baidu.com">支付成功<span>5</span>秒后跳转页面</a>
<script>
const a = document.querySelector("a")
num = 5
let timeId = setInterval(function(){
num--
a.innerHTML = `支付成功<span>${num}</span>秒后跳转页面`
if(num == 0){
clearInterval(timeId)
location.href = "https://www.baidu.com"
}
},1000)
</script>
</body>
navigator对象
// 检测 userAgent(浏览器信息)
!(function () {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动站点
if (android || iphone) {
location.href = 'http://m.itcast.cn'
}
})()
history对象
概述
本地存储类型——localStorage
作用:可以将数据永久存储在本地(用户的电脑),除非手动删除,否则关闭页面也会存在
特性
语法
localStorage修改数据 改(与增一致,存在则改,不存在则为增)
localStorage.setItem("name", "马浩楠")
console.log(localStorage.getItem("name"))
localStorage.removeItem("name")
浏览器查看位置
本地存储——sessionStorage
存储复杂数据类型
JSON.stringify(对象)
JSON.parse(json字符串)
<script>
const obj = {
name: "马浩楠",
age: 22
}
localStorage.setItem("obj",obj)
console.log(localStorage.getItem("obj")) // [object Object]这种方式无法存储复杂数据类型
// 将复杂数据类型转换为json字符串进行存储
localStorage.setItem("obj",JSON.stringify(obj))
console.log(localStorage.getItem("obj")) // {"name":"马浩楠","age":22}
// 将json字符串转换为对象
console.log(JSON.parse(localStorage.getItem("obj")))
</script>
const arr = ["red","blue","green"]
// 1.map方法:放回新的数组
const newArr = arr.map(function(ele,index){
console.log(ele) // 数组元素
console.log(index) // 数组索引
return ele + "颜色"
})
数据中的join方法
用于吧数组中的所有元素转换为一个字符串
参数:数组元素是通过参数里面指定的分隔符进行分割的
// 2.join方法:吧数组转换为字符串
// 参数为空则分隔符为逗号
console.log(newArr.join()) // red颜色,blue颜色,green颜色
console.log(newArr.join("")) // red颜色blue颜色green颜色
<body>
<h1>新增学员</h1>
<form class="info" autocomplete="off">
姓名:<input type="text" class="uname" name="uname" />
年龄:<input type="text" class="age" name="age" />
性别:
<select name="gender" class="gender">
<option value="男">男</option>
<option value="女">女</option>
</select>
薪资:<input type="text" class="salary" name="salary" />
就业城市:<select name="city" class="city">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="曹县">曹县</option>
</select>
<button class="add">录入</button>
</form>
<h1 class="info" style="text-align: right;">共有数据<span style="text-align:right;color: red;">几</span>条</h1>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>薪资</th>
<th>就业城市</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<!--
<tr>
<td>1001</td>
<td>欧阳霸天</td>
<td>19</td>
<td>男</td>
<td>15000</td>
<td>上海</td>
<td>
<a href="javascript:">删除</a>
</td>
</tr>
-->
</tbody>
</table>
<script>
// 参考数据
// let data = [{
// stuId: 1001,
// uname: '欧阳霸天',
// age: 19,
// gender: '男',
// salary: '20000',
// city: '上海',
// }]
let data = JSON.parse(localStorage.getItem("data"))
const uname = document.querySelector(".uname")
const age = document.querySelector(".age")
const gender = document.querySelector(".gender")
const salary = document.querySelector(".salary")
const city = document.querySelector(".city")
// 添加
const info = document.querySelector(".info")
info.addEventListener("submit", function () {
if(uname.value && age.value && gender.value && salary.value && city.value){
const obj = {
stuId: data.length ? +data[length-1].stuId + 1 : 1,
uname: uname.value,
age: age.value,
gender: gender.value,
salary: salary.value,
city: city.value
}
data.push(obj)
// 存储到本地
localStorage.setItem("data", JSON.stringify(data))
render()
}else{
alert("输入不能为空")
}
})
// 渲染
const tbody = document.querySelector("tbody")
render()
function render() {
tbody.innerHTML = ''
// 是空则为空数组
data = data ? JSON.parse(localStorage.getItem("data")) : []
// 几条数据
document.querySelector("span").innerHTML = `${data.length}`
// 利用map方法,将数据返回为tr形式
const newData = data.map(function (ele,index) {
return `
<tr>
<td>${ele.stuId}</td>
<td>${ele.uname}</td>
<td>${ele.age}</td>
<td>${ele.gender}</td>
<td>${ele.salary}</td>
<td>${ele.city}</td>
<td class='delete'>
<a href="javascript:" data-id=${index}>删除</a>
</td>
</tr>
`
})
// 将map方法进行处理过后的数组转为字符串形式来渲染tbody
tbody.innerHTML = newData.join("")
}
// 删除
tbody.addEventListener("click", function (e) {
if (e.target.tagName == "A") {
if (window.confirm("确定删除吗?")) {
for (let i = 0; i < data.length; i++) {
if (i == e.target.dataset.id) {
data.splice(i, 1)
localStorage.setItem("data", JSON.stringify(data))
}
}
}
}
render()
})
</script>
</body>
语法
const 变量名 = /表达式/
判断是否有符合规则的字符串:
检索符合规则的字符串
const str = "正在学习JS,赚钱学习路程中..."
// 定义规则
const reg = /学习/
// 匹配:返回true/false
console.log(reg.test(str))
// 匹配exec:返回数组/null
console.log(reg.exec(str))
元字符
注意逗号左右两则不要出现空格
字符类
<input type="text">
<span></span>
<script>
const input = document.querySelector("input")
const span = document.querySelector("span")
input.addEventListener("blur",function(){
const regex = /^[a-zA-Z0-9-_]{6,16}$/
span.className = ""
span.innerHTML = ""
if(!regex.test(input.value)){
span.innerText = "只能输入6~16位字符"
span.classList.add("error")
}else{
span.innerText = "输入正确"
span.classList.add("right")
}
})
</script>
修饰符
/表达式/修饰符
i 是单词 ignore 的缩写,正则匹配时字母不区分大小写
g 是单词 global 的缩写,匹配所有满足正则表达式的结果
replcae 替换
字符串.replace(/正则表达式/,"替换的文本")
// i:ignore忽略大小写
console.log(/^java$/.test("java")) // true
console.log(/^java$/.test("JAVA")) // false
console.log(/^java$/i.test("Java")) // true
// replace使用正则 g:全局查找 也可两个都写
const str = "Python是一门编程语言,Python is very nice"
console.log(str.replace(/python/gi,"Java"))
案例
<body>
<textarea name="" id="" cols="30" rows="10"></textarea>
<button>发布</button>
<div></div>
<script>
const text = document.querySelector("textarea")
const btn = document.querySelector("button")
const div = document.querySelector("div")
btn.addEventListener("click",function(){
div.innerHTML = text.value.replace(/激情/g,"**")
})
</script>
</body>
// 验证码部分
const code = document.querySelector(".code")
let isTrue = true
code.addEventListener("click",function(){
if(isTrue){
let i = 5
code.innerHTML = `0${i}后重新获取`
let timeId = setInterval(function(){
isTrue = false
i --
code.innerHTML = `0${i}后重新获取`
if(i == 0){
isTrue = true
code.innerHTML = "重新获取"
clearInterval(timeId)
}
},1000)
}
})
// 表单验证部分
// 1.名称
const username = document.querySelector("input[name='username']")
username.addEventListener("change",confirmUserName)
function confirmUserName(){
if(!/^[a-zA-Z0-9-_]{6,10}$/.exec(username.value.trim())){
username.nextElementSibling.innerHTML = "昵称长度为6-10个字符"
return false
}
username.nextElementSibling.innerHTML = ""
return true
}
// 2.手机号
const phone = document.querySelector("input[name='phone']")
phone.addEventListener("change",confirmPhone)
function confirmPhone(){
if(!/^1(3\d|4[5-9][5[0-35-9][6[567][7[0-8][8\d|9[0-35-9])\d{8}$/.test(phone.value.trim())){
phone.nextElementSibling.innerHTML = "请输入正确的手机号"
return false
}
phone.nextElementSibling.innerHTML = ""
return true
}
// 3.验证码
const inputCode = document.querySelector("input[name='code']")
inputCode.addEventListener("change",confirmCode)
function confirmCode(){
if(!/^\d{6}/.test(inputCode.value.trim())){
inputCode.nextElementSibling.innerHTML = "验证码错误"
return false
}
inputCode.nextElementSibling.innerHTML = ""
return true
}
// 4.密码
const pwd = document.querySelector("input[name='password']")
pwd.addEventListener("change",confirmPwd)
function confirmPwd(){
if(!/^[a-zA-Z0-9-_]{6,20}$/.test(pwd.value.trim())){
pwd.nextElementSibling.innerHTML = "设置6-20位字母、数字和符号组合"
return false
}
pwd.nextElementSibling.innerHTML = ""
return true
}
// 5.确认密码
const confirmAgainPwd = document.querySelector("input[name='confirm']")
confirmAgainPwd.addEventListener("change",confirmRepwd)
function confirmRepwd(){
if(confirmAgainPwd.value.trim() == pwd.value.trim()){
confirmAgainPwd.nextElementSibling.innerHTML = ""
return true
}
confirmAgainPwd.nextElementSibling.innerHTML = "两次密码不一致"
return false
}
// 确认协议
const icon = document.querySelector(".icon-queren")
icon.addEventListener("click",function(){
this.classList.toggle("icon-queren2")
})
// 提交
const form = document.querySelector(".xtx-form")
form.addEventListener("submit",function(e){
console.log(11)
if(!icon.classList.contains("icon-queren2")){
alert("请勾选同意协议")
e.preventDefault()
}
if(!confirmUserName()) e.preventDefault()
if(!confirmPhone()) e.preventDefault()
if(!confirmCode()) e.preventDefault()
if(!confirmPwd()) e.preventDefault()
if(!confirmRepwd()) e.preventDefault()
})
const tab_nav = document.querySelector(".tab-nav")
const pane = document.querySelectorAll(".tab-pane")
tab_nav.addEventListener("click",function(e){
if(e.target.tagName === "A"){
tab_nav.querySelector(".active").classList.remove("active")
e.target.classList.add("active")
// 将div都先隐藏
for(let i = 0;i<pane.length;i++){
pane[i].style.display = "none"
}
pane[e.target.dataset.id].style.display = "block"
}
})
// 登录
const form = document.querySelector("form")
const agree = document.querySelector("input[name='agree']")
const userName = document.querySelector("input[name='username']")
form.addEventListener("submit",function(e){
e.preventDefault()
if(!agree.checked){
return alert("请先勾选同意协议!")
}
// 将用户名存储到本地存储
localStorage.setItem("userName",userName.value)
// 跳转页面
window.location.href = "./index.html"
})
const firstLi = document.querySelector("ul[class='xtx_navs'] li:first-child")
const secondLi = firstLi.nextElementSibling
// 渲染
function render(){
const userName = localStorage.getItem("userName")
if(userName){
firstLi.innerHTML = `<a href="javascript:;"><i class="iconfont icon-user">${userName}</i></a>`
secondLi.innerHTML = '<a href="javascript:;">退出登录</a>'
}else{
firstLi.innerHTML = `<a href="./login.html">请先登录</a>`
secondLi.innerHTML = '<a href="./register.html">免费注册</a>'
}
}
render()
secondLi.addEventListener("click",function(){
localStorage.removeItem("userName")
render()
})
(function(){
const sticky = document.querySelector(".sticky")
const header = document.querySelector(".xtx_header .wrapper")
window.addEventListener("scroll",function(){
const n = document.documentElement.scrollTop
sticky.style.top = n >= header.offsetTop ? "0px" : "-80px"
})
})();
// 图片
(function(){
const ul = document.querySelector(".small ul")
const img = document.querySelector(".middle img")
// mouseenter 没有冒泡 mouseover存在
ul.addEventListener("mouseover",function(e){
if(e.target.tagName === "IMG"){
this.querySelector(".active").classList.remove("active")
e.target.parentNode.classList.add("active")
img.src = e.target.src
large.style.backgroundImage = `url(${e.target.src})`
}
})
// 回到顶部
const backTop = document.querySelector(".backTop")
backTop.addEventListener("click",function(){
document.documentElement.scrollTo(0,0)
})
})();
放大镜效果
①:鼠标经过对应小盒子,左侧中等盒子显示对应中等图片
②: 鼠标经过中盒子,右侧会显示放大镜效果的大盒子
③: 黑色遮罩盒子跟着鼠标来移动
④: 鼠标在中等盒子上移动,大盒子的图片跟着显示对应位置
解析
③: 黑色遮罩盒子跟着鼠标来移动
先做鼠标经过 中等盒子,显示隐藏 黑色遮罩 的盒子
让黑色遮罩跟着鼠标来走, 需要用到鼠标移动事件 mousemove
让黑色盒子的移动的核心思想:不断把鼠标在中等盒子内的坐标给黑色遮罩层 let top 值,这样遮罩层就可以跟着移动了
需求
算法
注意 y坐标特殊,需要减去 页面被卷去的头部
为什么不用 box.offsetLet 和 box.offsetTop 因为这俩属性跟带有定位的父级有关系,
很容被父级影响,而getBoundingClientRect() 不受定位的父元素的影响
限定遮罩的盒子只能在middle 内部移动,需要添加判断
遮罩盒子移动的坐标:
let mx = 0, my = 0;
if (x <= 100) mx = 0
if (x > 100 && x < 300) mx = x - 100
if (x >= 300) mx = 200
if (y <= 100) my = 0
if (y > 100 && y < 300) my = y - 100
if (y >= 300) my = 200
large.style.backgroundPositionX = - 2 * mx + 'px'
large.style.backgroundPositionY = - 2 * my + 'px'
const small = document.querySelector(".small")
const middle = document.querySelector(".middle")
const large = document.querySelector(".large")
// 鼠标经过中等盒子显示大盒子
middle.addEventListener("mouseenter",show)
middle.addEventListener("mouseleave",hide)
let timerId = null
function show(){
clearTimeout(timerId)
large.style.display = "block"
}
function hide(){
timerId = setTimeout(function(){
large.style.display = "none"
},200)
}
// 鼠标经过大盒子,显示隐藏 大盒子
large.addEventListener("mouseenter",show)
large.addEventListener("mouseout",hide)
// 放大镜:黑色遮罩
const layer = document.querySelector(".layer")
middle.addEventListener("mousemove",function(){
layer.style.display = "block"
})
middle.addEventListener("mouseleave",function(){
layer.style.display = "none"
})
// 放大镜:移动黑色遮罩
middle.addEventListener("mousemove",function(e){
// 鼠标在middle 盒子里面的坐标 = 鼠标在页面中的坐标(e.pageX/e.pageY) - middle中等盒子的坐标(getBoundingClientRect())
let x = e.pageX - middle.getBoundingClientRect().left
let y = e.pageY - middle.getBoundingClientRect().top - document.documentElement.scrollTop
//layer.style.left = x >= 200 ? "200px" :x + "px"
//layer.style.top = y >= 200 ? "200px" : y + "px"
let mx = 0 , my = 0
if(x <= 100) mx = 0
if(x > 100 && mx < 300) mx = x - 100
if(x >= 300) mx = 200
if(y <= 100) my = 0
if(y > 100 && my < 300) my = y - 100
if(y >= 300) my = 200
layer.style.left = mx + "px"
layer.style.top = my + "px"
// 大盒子的背景图片要跟随中等盒子移动 存在关系是2倍
large.style.backgroundPositionX = -2 * mx +"px"
large.style.backgroundPositionY = -2 * my +"px"
})
// 点击模块
(function(){
const size = document.querySelector("#size")
size.addEventListener("click",function(e){
if(e.target.tagName === "SPAN"){
size.querySelector(".active").classList.remove("active")
e.target.classList.add("active")
}
})
// 加减物品数量
const num = document.querySelector(".num")
const decrease = num.children[0]
const number = num.children[1]
const increase = num.children[2]
decrease.addEventListener("click",function(){
if(number.value == 1){
this.disabled = true
return
}
number.value -= 1
})
increase.addEventListener("click",function(){
number.value = +number.value +1
})
// tab切换
const tab = document.querySelector(".tab-head")
const panes = document.querySelectorAll(".cont .tab-pane")
tab.addEventListener("click",function(e){
if(e.target.tagName === "A"){
for(let i = 0;i<panes.length;i++){
panes[i].style.display = "none"
}
panes[e.target.dataset.id].style.display = "block"
}
})
})();